<?php

abstract class Yz_Template_Smarty
    extends Yz_Template_Abstract
    implements Yz_Template_Interface
{


    // required constant
    const TEMPLATE_TYPE = 'tpl';

    /** paths to smarty cache
     *  relative to global cachePath
     */
    const CACHE_DIR = 'smarty/cache/';
    const CACHE_TEMPLATES = 'smarty/templates_c/';


    /**
     *  @var Smarty the instance of Smarty
     */
    protected static $smarty;

    /** Reference to the current template being rendered
     *  @var Yz_Template_Interface
     */
    protected static $currentTemplate;



    /****************************
     *      Public methods      *
     ****************************/

    /**
     *
     *  template search order:
     *  - lib/templates
     *  - $moduleDir/templates
     *
     */
    protected function doRender()
    {
        // find the file
        $file = $this->getFile();
        debug("Template '{$file}'");
        // reassign current template
        $oldCurrent = self::$currentTemplate;
        self::$currentTemplate = $this;

        $smarty = self::useSmarty();
        $smarty->assign($this->value);
        $smarty->assign('title', $this->getTitle());

        // ### this is wrong since the included templates will overwrite this var
        // ### replace by plugins !!!
        // $smarty->assign("Yz_Template", $this);
        try {
            $o = $smarty->fetch(
                "file:{$file}",
                null,   # cache_id
                strtr($this->namespaceName(), '\\', '.') # compile_id
            );
        } catch (SmartyException $ex) {
            debug( "SmartyException: " . $ex );
            $ex = new Yz_Template_Exception();
            throw $ex->withArgs(array("name"=>$this->shortClassName()));
        }
        // restore previous current template
        self::$currentTemplate = $oldCurrent;
        return $o;
    }

    /****************************
     *      Smarty plugins      *
     ****************************/


    /**
     *  parameters to {includeJS}:
     *      src     (may be empty)
     *  content is the SCRIPT tag content
     *
     *  @return ""
     */
    public static function smartyIncludeJS($params, $content, $smarty, &$repeat)
    {
        debug($content);
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        if (null !== $content) {
            "" === $content and $content = null;
            $src = array_key_exists('src', $params) ? $params['src'] : 'js'.rand(1000000,9999999);
            $t->includeJS($src, $content);
        }
        return "";
    }


    /**
     *
     */
    public static function smartyRenderJS($params, $smarty)
    {
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        return $t->renderJS();
    }


    /**
     *
     */
    public static function smartyRenderCSS($params, $smarty)
    {
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        return $t->renderCSS();
    }

    /**
     *
     */
    public static function smartyRenderForm($params, $smarty)
    {
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        if (array_key_exists('name', $params)) {
            $formName = $params['name'];
        } else {
            $formName = 'default';
        }
        return $t->renderForm($formName);
    }

    /**
     *
     */
    public static function smartyRenderTemplate($params, $smarty)
    {
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        if (! array_key_exists('name', $params)) {
            return false;
        }
        if (array_key_exists('module', $params)) {
            $module = $params['module'];
        } else {
            $module = null;
        }
        return $t->renderTemplate($params['name'], $module);
    }


    /**
     *
     */
    public static function smartyTemplateName($params, $smarty)
    {
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        return $t->className();
    }

    public static function smartyTranslateBlock($params, $content, $smarty, &$repeat)
    {
        debug($content);
        $t = self::$currentTemplate;
        assert('null !== $t'); // the method may not be called outside smarty renderer
        if (null !== $content) {
            $lang = array_key_exists('lang', $params)
                ? $params['lang']
                : null;
            return $t->translate($content, $lang);
        }
        return $content;
    }


    /****************************
     *      Protected methods   *
     ****************************/



    protected static function useSmarty()
    {
        debug();
        $smarty =& self::$smarty;
        if (null === $smarty) {
            $cfg = Yz::getConfig();
            $installPath = $cfg->fetchString('smarty.installPath');
            if (! $installPath || ! is_dir($installPath)) {
                throw new Yz_Exception("Smarty not installed properly");
            }
            // the smarty autoloader is not smart at all and requires a couple of hacks
            // to work correctly with ours:
            // load the parent class of smarty
            require_once($installPath . "/sysplugins/smarty_internal_data.php");
            require_once($installPath . "/Smarty.class.php");
            // correct autoload: place it before the Yz.autoload, to avoid ClassNotFound exceptions
            spl_autoload_unregister('smartyAutoload');
            spl_autoload_register('smartyAutoload', true, true);
            //debug("Smarty class file loaded");

            // global cache path
            $cachePathGlobal = Yz_Cache_Default::getCachePath();
            $cachePath = str_replace(
                '%{cachePath}',
                $cachePathGlobal,
                $cfg->fetchString('smarty.cachePath', $cachePathGlobal)
            );
            $cacheDir = $cachePath . self::CACHE_DIR;
            if (! is_dir($cacheDir)) {
                throw new Yz_Exception("Smarty cache directory does not exist");
            }
            $compileDir = $cachePath . self::CACHE_TEMPLATES;
            if (! is_dir($compileDir)) {
                throw new Yz_Exception("Smarty compiled templates directory does not exist");
            }
            if (! is_writable($compileDir)) {
                throw new Yz_Exception("Smarty compiled templates directory is not writable");
            }

            $smarty = new Smarty();
            $dir = Yz::getAppNamespace();
            $smarty->template_dir = YZ_LIB . $dir . '/templates/';
            $smarty->cache_dir    = $cacheDir;
            $smarty->compile_dir  = $compileDir;
            $smarty->config_dir   = YZ_LIB . 'configs/';

            //$class = get_called_class();

            $smarty->registerPlugin("function","renderCSS", array(__CLASS__, 'smartyRenderCSS'));
            $smarty->registerPlugin("function","renderJS", array(__CLASS__, 'smartyRenderJS'));
            $smarty->registerPlugin("function","renderForm", array(__CLASS__, 'smartyRenderForm'));
            $smarty->registerPlugin("function","renderTemplate", array(__CLASS__, 'smartyRenderTemplate'));
            $smarty->registerPlugin("function","templateName", array(__CLASS__, 'smartyTemplateName'));
            $smarty->registerPlugin("block","translate", array(__CLASS__, 'smartyTranslateBlock'));
            $smarty->registerPlugin("block","includeJS", array(__CLASS__, 'smartyIncludeJS'));
        }
        return $smarty;
    }

}
